/*
 * InfrastructureMap.cpp
 *
 *  Created on: Nov 20, 2008
 *      Author: Ryan
 */

#include "InfrastructureMap.h"

#include "../UnitAnalyzers/BuildingFilter.h"
#include "../UnitAnalyzers/FactoryFilter.h"

#include "../../../OOInterface/Objects/Units/UnitAspects/BuildingAspect.h"

InfrastructureMap::InfrastructureMap( const OOInterface::GameMap& gameMap ) :
	_gameMap( gameMap )
{
}

InfrastructureMap::~InfrastructureMap()
{
}

bool InfrastructureMap::canBuildAt( OOInterface::ConstUnitPtr building, OOInterface::Point3f loc )
{
	BuildingFilter filter;
	building->accept( filter );

	if( !filter.isBuilding() )
		return false;

	const OOInterface::BuildingAspect* buildingAspect = filter.getBuildingAspect();

	bool buildingOK = buildingAspect->canBuildAt( _gameMap , loc, OOInterface::Direction(0.0f) );

	return buildingOK && isAreaEmpty( building, loc );
}

bool InfrastructureMap::isAreaEmpty( OOInterface::ConstUnitPtr building, OOInterface::Point3f loc )
{
	//Check to see if this new building intersects with any existing buildings
	//Simple Square bounding check against all existing built buildings
	//(probably should filter out buildings that are too far but whatever)

	int factorySpacing = 4; //Space out the Factories so they have breathing room for units they produce

	FactoryFilter factory;

	factory.reset();
	building->accept( factory );

	float myDiagonal = sqrt( building->getXSize() * building->getXSize() + building->getZSize() * building->getZSize() );

	myDiagonal += 1.0f;

	if( factory.isFactory() )
		myDiagonal += factorySpacing;


	float myX = loc.getX();
	float myZ = loc.getZ();

	for( BuildingLocPairs::iterator built = _buildings.begin(); built != _buildings.end(); ++built )
	{
		if(!(*built).first.get())
			continue;

		factory.reset();

		OOInterface::ConstUnitPtr builtBuilding = (*built).first;
		OOInterface::Point3f buildingLoc = (*built).second;
		builtBuilding->accept( factory );

		float diagonal = sqrt( builtBuilding->getXSize() * builtBuilding->getXSize() + builtBuilding->getZSize() * builtBuilding->getZSize() );
		diagonal += 1.0f;

		if( factory.isFactory() )
			diagonal += factorySpacing;

		float x = buildingLoc.getX();
		float z = buildingLoc.getZ();
		float distanceBetween = std::sqrt( std::pow( myX - x, x) + std::pow( myZ - z, 2 ) );

		if( distanceBetween < diagonal + myDiagonal )
			return false;


	}

	return true;
}

OOInterface::Point3f InfrastructureMap::findNearbyBuildLocation( OOInterface::ConstUnitPtr building, OOInterface::Point3f loc )
{
	int xVariance = _gameMap.getWidth() / 4;

	OOInterface::Point3f buildLoc;

	for( int x = 0; x < (xVariance/2); ++x )
	{
		for( int y = 0; y < x; ++y )
		{
			buildLoc.setX( x + loc.getX());
			buildLoc.setZ( y + loc.getZ());
			if( buildLoc.getX() < 0 || buildLoc.getX() >= _gameMap.getWidth() || buildLoc.getZ() < 0 || buildLoc.getZ() >= _gameMap.getHeight() )
			{
			}
			else
			{
				buildLoc.setY( _gameMap.getElevation( buildLoc.getX(), buildLoc.getZ()));
				if( canBuildAt( building, buildLoc) )
					return buildLoc;
			}


			int y2 = -1*y;
			buildLoc.setZ( y2 + loc.getZ());
			if( buildLoc.getX() < 0 || buildLoc.getX() >= _gameMap.getWidth() || buildLoc.getZ() < 0 || buildLoc.getZ() >= _gameMap.getHeight() )
				continue;
			buildLoc.setY( _gameMap.getElevation( buildLoc.getX(), buildLoc.getZ()));
			if( canBuildAt( building, buildLoc) )
				return buildLoc;
		}
		int x2 = -1 *x;
		for( int y = 0; y < x; ++y )
		{
			buildLoc.setX( x2 + loc.getX());
			buildLoc.setZ( y + loc.getZ());
			if( buildLoc.getX() < 0 || buildLoc.getX() >= _gameMap.getWidth() || buildLoc.getZ() < 0 || buildLoc.getZ() >= _gameMap.getHeight() )
			{
			}
			else
			{
				buildLoc.setY( _gameMap.getElevation( buildLoc.getX(), buildLoc.getZ()));
				if( canBuildAt( building, buildLoc) )
					return buildLoc;
			}


			int y2 = -1*y;
			buildLoc.setZ( y2 + loc.getZ());
			if( buildLoc.getX() < 0 || buildLoc.getX() >= _gameMap.getWidth() || buildLoc.getZ() < 0 || buildLoc.getZ() >= _gameMap.getHeight() )
				continue;
			buildLoc.setY( _gameMap.getElevation( buildLoc.getX(), buildLoc.getZ()));
			if( canBuildAt( building, buildLoc) )
				return buildLoc;
		}
	}

	buildLoc.setX(-1); buildLoc.setZ(-1);
	return buildLoc;
}

bool InfrastructureMap::buildBuilding( OOInterface::ConstUnitPtr building )
{
	return buildBuilding( building, building->getLocation() );
}

bool InfrastructureMap::buildBuilding( OOInterface::ConstUnitPtr building, OOInterface::Point3f loc )
{
	if( !canBuildAt( building, loc ) && building->getHealth() <= 0.0f )
		return false;

	_buildings.push_front( std::pair< OOInterface::ConstUnitPtr, OOInterface::Point3f > (building, loc ) );

	return true;
}

void InfrastructureMap::destroyBuilding( OOInterface::ConstUnitPtr building )
{
	for( BuildingLocPairs::iterator built = _buildings.begin(); built != _buildings.end(); ++built )
	{
		if( (*built).first->getID() == building->getID() )
		{
			_buildings.erase( built );
			return;
		}
	}
}
